<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>RAG 端到端评测平台 (横向对比专业版)</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Inter', 'Helvetica Neue', 'Arial', 'sans-serif';
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
        }
        
        /* 现代化渐变背景 */
        .bg-gradient-main {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        }
        
        /* 玻璃态效果 */
        .glass-morphism {
            background: rgba(255, 255, 255, 0.95);
            backdrop-filter: blur(20px);
            border: 1px solid rgba(255, 255, 255, 0.2);
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
        }
        
        /* 现代化卡片设计 */
        .global-card, .experiment-column {
            background: rgba(255, 255, 255, 0.95);
            backdrop-filter: blur(20px);
            border-radius: 1.5rem;
            border: 1px solid rgba(255, 255, 255, 0.2);
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.08), 0 8px 25px rgba(0, 0, 0, 0.06);
            transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
        }
        
        .global-card:hover, .experiment-column:hover {
            transform: translateY(-2px);
            box-shadow: 0 32px 80px rgba(0, 0, 0, 0.12), 0 12px 30px rgba(0, 0, 0, 0.08);
        }
        
        .experiment-column {
            display: flex;
            flex-direction: column;
            width: 100%;
            position: relative;
            overflow: hidden;
        }
        
        /* 添加顶部装饰条 */
        .experiment-column::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 4px;
            background: linear-gradient(90deg, #667eea, #764ba2, #f093fb);
            border-radius: 1.5rem 1.5rem 0 0;
        }
        
        /* 现代化结果卡片 */
        .result-card {
            background: linear-gradient(145deg, #f8faff 0%, #f1f5ff 100%);
            border: 1px solid rgba(102, 126, 234, 0.1);
            border-radius: 1rem;
            padding: 1rem;
            margin-top: 0.75rem;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            position: relative;
            overflow: hidden;
        }
        
        .result-card::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
            transition: left 0.6s ease;
        }
        
        .result-card:hover::before {
            left: 100%;
        }
        
        .result-card:hover {
            transform: translateY(-1px);
            box-shadow: 0 12px 24px rgba(102, 126, 234, 0.15);
            border-color: rgba(102, 126, 234, 0.2);
        }
        
        /* 现代化加载动画 */
        .loader {
            width: 20px;
            height: 20px;
            border: 3px solid rgba(255, 255, 255, 0.3);
            border-radius: 50%;
            border-top-color: #ffffff;
            animation: spin 1s linear infinite;
        }
        
        @keyframes spin {
            to { transform: rotate(360deg); }
        }
        
        /* 渐变加载动画 */
        .gradient-loader {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background: conic-gradient(#ffffff, #667eea);
            animation: rotate 1s linear infinite;
        }
        
        @keyframes rotate {
            to { transform: rotate(360deg); }
        }
        
        /* 现代化来源标签 */
        .source-tag {
            font-size: 0.7rem;
            padding: 0.25rem 0.5rem;
            border-radius: 0.5rem;
            font-weight: 600;
            margin-left: 0.5rem;
            background: linear-gradient(135deg, var(--tag-bg-start), var(--tag-bg-end));
            color: var(--tag-text);
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
            transition: all 0.3s ease;
        }
        
        .source-tag:hover {
            transform: scale(1.05);
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        }
        
        /* 切换图标动画 */
        .toggle-icon {
            transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        }
        
        /* 现代化按钮设计 */
        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            border: none;
            border-radius: 1rem;
            padding: 0.75rem 1.5rem;
            color: white;
            font-weight: 600;
            font-size: 0.9rem;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
            position: relative;
            overflow: hidden;
        }
        
        .btn-primary::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
            transition: left 0.6s ease;
        }
        
        .btn-primary:hover::before {
            left: 100%;
        }
        
        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
        }
        
        .btn-primary:active {
            transform: translateY(0);
        }
        
        .btn-secondary {
            background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
            border: none;
            border-radius: 1rem;
            padding: 0.75rem 1.5rem;
            color: #4a5568;
            font-weight: 600;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            box-shadow: 0 4px 15px rgba(168, 237, 234, 0.3);
            position: relative;
            overflow: hidden;
        }
        
        .btn-secondary::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
            transition: left 0.6s ease;
        }
        
        .btn-secondary:hover::before {
            left: 100%;
        }
        
        .btn-secondary:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 25px rgba(168, 237, 234, 0.4);
        }
        
        .btn-danger {
            background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
            color: white;
        }
        
        .btn-reset {
            background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
            border: none;
            border-radius: 1rem;
            padding: 0.75rem 1.5rem;
            color: white;
            font-weight: 600;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            box-shadow: 0 4px 15px rgba(251, 191, 36, 0.3);
            position: relative;
            overflow: hidden;
        }
        
        .btn-reset::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
            transition: left 0.6s ease;
        }
        
        .btn-reset:hover::before {
            left: 100%;
        }
        
        .btn-reset:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 25px rgba(251, 191, 36, 0.4);
        }
        
        /* 现代化表单元素 */
        .form-input, .form-select, .form-textarea {
            border: 2px solid rgba(102, 126, 234, 0.1);
            border-radius: 0.75rem;
            padding: 0.75rem 1rem;
            background: rgba(255, 255, 255, 0.8);
            transition: all 0.3s ease;
            backdrop-filter: blur(10px);
        }
        
        .form-input:focus, .form-select:focus, .form-textarea:focus {
            outline: none;
            border-color: #667eea;
            box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
            background: rgba(255, 255, 255, 0.95);
        }
        
        /* 现代化复选框 */
        .modern-checkbox {
            appearance: none;
            width: 1.25rem;
            height: 1.25rem;
            border: 2px solid #d1d5db;
            border-radius: 0.375rem;
            background: white;
            cursor: pointer;
            transition: all 0.3s ease;
            position: relative;
        }
        
        .modern-checkbox:checked {
            background: linear-gradient(135deg, #667eea, #764ba2);
            border-color: #667eea;
        }
        
        .modern-checkbox:checked::after {
            content: '✓';
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: white;
            font-size: 0.75rem;
            font-weight: bold;
        }
        
        /* 悬浮效果增强 */
        .hover-lift {
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        }
        
        .hover-lift:hover {
            transform: translateY(-2px);
        }
        
        /* 渐变文字 */
        .gradient-text {
            background: linear-gradient(135deg, #667eea, #764ba2);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }
        
        /* 分区装饰 */
        .section-divider {
            background: linear-gradient(90deg, transparent, rgba(102, 126, 234, 0.3), transparent);
            height: 1px;
            margin: 1.5rem 0;
        }
        
        /* 状态指示器 */
        .status-indicator {
            display: inline-block;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            margin-right: 0.5rem;
            animation: pulse 2s infinite;
        }
        
        .status-running {
            background: #f59e0b;
        }
        
        .status-success {
            background: #10b981;
        }
        
        .status-error {
            background: #ef4444;
        }
        
        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; }
        }
        
        /* 添加实验按钮特殊样式 */
        #add-experiment-btn {
            border: 2px dashed rgba(102, 126, 234, 0.3);
            background: rgba(255, 255, 255, 0.5);
            backdrop-filter: blur(10px);
            transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
        }
        
        #add-experiment-btn:hover {
            border-color: #667eea;
            background: rgba(102, 126, 234, 0.05);
            transform: scale(1.02);
        }
        
        /* 评分显示优化 */
        .score-display {
            background: linear-gradient(135deg, #f0f9ff, #e0f2fe);
            border-radius: 0.5rem;
            padding: 0.5rem;
            text-align: center;
            border: 1px solid rgba(59, 130, 246, 0.2);
        }
        
        /* 响应式优化 */
        @media (max-width: 768px) {
            .global-card, .experiment-column {
                border-radius: 1rem;
                margin: 0.5rem;
            }
            
            .result-card {
                border-radius: 0.75rem;
                padding: 0.75rem;
            }
        }
        
        /* 头部优化 */
        .header-gradient {
            background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
            backdrop-filter: blur(10px);
            border-radius: 1.5rem;
            padding: 2rem;
            margin-bottom: 2rem;
            border: 1px solid rgba(255, 255, 255, 0.2);
        }
        
        .header-title {
            background: linear-gradient(135deg, #ffffff, #f8faff);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            text-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }
    </style>
</head>
<body class="text-gray-800">

    <div class="container mx-auto p-4 md:p-6">
        <header class="text-center header-gradient">
            <h1 class="text-3xl md:text-4xl font-bold header-title">RAG 端到端评测平台</h1>
            <p class="mt-2 text-md text-white/80">支持多路实验横向对比</p>
        </header>

        <!-- 全局配置 -->
        <div class="global-card p-6 mb-6">
            <!-- Global Inputs -->
            <div class="grid grid-cols-1 md:grid-cols-3 gap-6 items-end">
                <div class="md:col-span-2">
                    <label for="question" class="block text-sm font-medium text-gray-700 mb-2">用户问题</label>
                    <textarea id="question" rows="2" class="form-textarea mt-1 block w-full" placeholder="例如：公司今年的年假政策是什么？">公司今年的年假政策是什么？</textarea>
                </div>
                <div>
                     <label for="knowledge-base" class="block text-sm font-medium text-gray-700 mb-2">知识库</label>
                    <select id="knowledge-base" class="form-select mt-1 block w-full">
                        <option>人力资源政策库</option>
                        <option>财务报销制度库</option>
                    </select>
                </div>
            </div>
            
            <div class="section-divider"></div>
            
            <!-- Generation Config -->
            <div class="mt-4">
                 <h3 class="text-lg font-medium gradient-text mb-4">全局生成配置</h3>
                 <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div>
                        <label for="prompt-template" class="block text-sm font-medium text-gray-700 mb-2">提示词模板</label>
                         <textarea id="prompt-template" class="form-textarea mt-1 block w-full" rows="4">请根据以下背景知识，简洁、准确地回答用户的问题。

【背景知识】:
{context}

【用户问题】:
{question}

【你的回答】:</textarea>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 mb-3">选择对比的大模型</label>
                        <div class="mt-2 flex flex-col space-y-3">
                            <label class="flex items-center hover-lift cursor-pointer">
                                <input type="checkbox" class="modern-checkbox llm-checkbox" value="GPT-4o" checked> 
                                <span class="ml-3 text-sm font-medium">GPT-4o</span>
                            </label>
                            <label class="flex items-center hover-lift cursor-pointer">
                                <input type="checkbox" class="modern-checkbox llm-checkbox" value="Qwen-Max" checked> 
                                <span class="ml-3 text-sm font-medium">通义千问-Max</span>
                            </label>
                            <label class="flex items-center hover-lift cursor-pointer">
                                <input type="checkbox" class="modern-checkbox llm-checkbox" value="Claude 3 Opus"> 
                                <span class="ml-3 text-sm font-medium">Claude 3 Opus</span>
                            </label>
                        </div>
                    </div>
                 </div>
            </div>
            
            <div class="section-divider"></div>
            
            <!-- Main Action Button -->
            <div class="mt-4 flex items-center space-x-4">
                 <button id="main-flow-btn" class="btn-primary flex items-center justify-center transition-all duration-300">
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
                    运行所有实验
                </button>
                <button id="reset-btn" class="btn-reset flex items-center justify-center transition-all duration-300 hidden">
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/><path d="M21 21v-5h-5"/></svg>
                    重置实验
                </button>
                <p id="run-status" class="text-sm text-gray-600 flex items-center"></p>
            </div>
        </div>

        <!-- 实验对比区 -->
        <div id="experiments-container" class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
            <!-- Experiment columns will be injected here -->
        </div>

    </div>

    <!-- Template for an experiment column -->
    <template id="experiment-template">
        <div class="experiment-column p-6">
            <div class="flex-grow">
                <div class="flex justify-between items-center mb-6">
                    <h3 class="text-xl font-semibold gradient-text experiment-title">实验 1</h3>
                    <button class="remove-experiment-btn text-gray-400 hover:text-red-500 hover-lift p-2 rounded-lg hover:bg-red-50 transition-all" title="移除实验">
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
                    </button>
                </div>

                <!-- Config -->
                <div class="space-y-4 p-5 border-2 border-gray-100 rounded-xl bg-gradient-to-br from-gray-50 to-blue-50">
                    <h4 class="font-semibold text-gray-800 flex items-center">
                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2 text-blue-500"><circle cx="12" cy="12" r="3"></circle><path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"></path></svg>
                        实验配置
                    </h4>
                    <!-- Recall Config -->
                    <div>
                        <label class="block text-xs font-medium text-gray-700 mb-2">召回策略</label>
                        <select class="retrieval-strategy form-select block w-full text-sm">
                            <option value="hybrid">混合检索</option>
                            <option value="vector">向量检索</option>
                            <option value="keyword">关键词检索</option>
                        </select>
                    </div>
                    <div>
                        <label class="block text-xs font-medium text-gray-700 mb-2">向量化模型</label>
                        <select class="embedding-model form-select block w-full text-sm">
                            <option>BGE-large-zh-v1.5</option>
                            <option>M3E-large</option>
                        </select>
                    </div>
                    <!-- Rerank Config -->
                    <div class="border-t border-gray-200 pt-4 mt-4">
                        <label class="block text-xs font-medium text-gray-700 mb-2">重排模型</label>
                        <select class="rerank-model form-select block w-full text-sm">
                            <option>不使用重排</option>
                            <option>BGE-Reranker-large</option>
                            <option>Qwen3_4B-Reranker</option>
                        </select>
                        <div class="grid grid-cols-2 gap-3 mt-3">
                            <div>
                                <label class="block text-xs font-medium text-gray-700 mb-1">Top K</label>
                                <input type="number" class="rerank-topk form-input block w-full text-sm" value="5">
                            </div>
                            <div>
                                <label class="block text-xs font-medium text-gray-700 mb-1">阈值</label>
                                <input type="number" step="0.01" class="rerank-threshold form-input block w-full text-sm" value="0.50">
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Results -->
                <div class="results-container mt-6 space-y-4 hidden">
                    <!-- Recall Results -->
                    <div class="recall-section border-t border-gray-200 pt-4">
                        <div class="section-toggle flex justify-between items-center cursor-pointer p-2 -m-2 rounded-lg hover:bg-blue-50 transition-all">
                            <h5 class="font-semibold text-gray-700 flex items-center">
                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2 text-blue-500"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path><polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline><line x1="12" y1="22.08" x2="12" y2="12"></line></svg>
                                召回结果 (Top 10)
                            </h5>
                            <svg class="toggle-icon h-5 w-5 text-gray-500 transform -rotate-90 transition-transform" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" /></svg>
                        </div>
                        <div class="section-content hidden">
                            <div class="recall-metrics result-card hidden"></div>
                            <div class="recall-list mt-3 space-y-2 text-sm"></div>
                        </div>
                    </div>
                    <!-- Rerank Results -->
                    <div class="rerank-section border-t border-gray-200 pt-4">
                        <div class="section-toggle flex justify-between items-center cursor-pointer p-2 -m-2 rounded-lg hover:bg-purple-50 transition-all">
                            <h5 class="font-semibold text-gray-700 flex items-center">
                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2 text-purple-500"><path d="M3 6h18l-2-3H5l-2 3zM19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m8 0V4a2 2 0 0 0-2-2H9a2 2 0 0 0-2 2v2"></path></svg>
                                重排结果
                            </h5>
                             <svg class="toggle-icon h-5 w-5 text-gray-500 transform -rotate-90 transition-transform" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" /></svg>
                        </div>
                        <div class="section-content hidden">
                            <div class="rerank-metrics result-card hidden"></div>
                            <div class="rerank-list mt-3 space-y-2 text-sm"></div>
                        </div>
                    </div>
                     <!-- Generation Results -->
                    <div class="generation-section border-t border-gray-200 pt-4">
                        <div class="section-toggle flex justify-between items-center cursor-pointer p-2 -m-2 rounded-lg hover:bg-green-50 transition-all">
                            <h5 class="font-semibold text-gray-700 flex items-center">
                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2 text-green-500"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14,2 14,8 20,8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10,9 9,9 8,9"></polyline></svg>
                                生成答案
                            </h5>
                             <svg class="toggle-icon h-5 w-5 text-gray-500 transform -rotate-90 transition-transform" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" /></svg>
                        </div>
                        <div class="section-content hidden">
                            <div class="generation-output result-card bg-white border-2 border-green-100"></div>
                        </div>
                    </div>
                </div>
                <div class="placeholder-container flex flex-col items-center justify-center h-64 text-gray-400">
                    <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" class="mb-4 opacity-50"><circle cx="12" cy="12" r="3"></circle><path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"></path></svg>
                    <p class="text-lg font-medium">等待运行...</p>
                    <p class="text-sm opacity-70 mt-1">点击上方按钮开始实验</p>
                </div>
            </div>
        </div>
    </template>


    <script>
    document.addEventListener('DOMContentLoaded', () => {
        const experimentsContainer = document.getElementById('experiments-container');
        const experimentTemplate = document.getElementById('experiment-template');
        const mainFlowBtn = document.getElementById('main-flow-btn');
        const resetBtn = document.getElementById('reset-btn');
        const runStatus = document.getElementById('run-status');
        const globalPromptTemplate = document.getElementById('prompt-template');

        let experimentCounter = 0;
        
        let appState = {
            step: 'initial', // initial, recalled, scored
            uniqueDocs: new Map(),
            goldenScores: {},
            experiments: new Map()
        };

        const mockKnowledgeBase = {
            '人力资源政策库': [
                { id: 'hr-03', content: '关于年假，所有入职满一年的正式员工享有10天带薪年假。', source: 'both' },
                { id: 'hr-08', content: '员工的年假天数会根据服务年限进行调整，每满五年增加一天。', source: 'vector' },
                { id: 'hr-01', content: '公司的标准工作时间为周一至周五，上午9点至下午6点。', source: 'keyword' },
                { id: 'hr-05', content: '新员工的试用期为三个月，试用期内不享有年假。', source: 'vector' },
                { id: 'hr-12', content: '年假申请需要提前两周在HR系统提交，由直属上级和HR部门审批。', source: 'both' },
                { id: 'hr-02', content: '加班餐补标准为30元/人，需在晚上8点后申请。', source: 'keyword' },
                { id: 'hr-09', content: '年度体检为公司福利，所有员工均可参加。', source: 'keyword' },
                { id: 'hr-15', content: '差旅报销需提供有效发票，并在行程结束后一周内提交。', source: 'vector' },
                { id: 'hr-04', content: '员工生日会于每月最后一个周五举行。', source: 'keyword' },
                { id: 'hr-07', content: '未休年假可在年底申请折算为三倍工资。', source: 'vector' }
            ],
            '财务报销制度库': [
                { id: 'fin-01', content: '所有报销必须提供正规发票，电子发票需打印。'},
                { id: 'fin-02', content: '差旅费报销标准：交通实报实销，住宿一线城市800元/晚。'},
                { id: 'fin-03', content: '招待费用报销需提前申请，并附上招待事由和人员名单。'}
            ]
        };

        const mockGoldenScoresData = { 'hr-03': 0.95, 'hr-08': 0.88, 'hr-01': 0.10, 'hr-05': 0.75, 'hr-12': 0.92, 'hr-02': 0.05, 'hr-09': 0.02, 'hr-15': 0.01, 'hr-04': 0.01, 'hr-07': 0.98, 'fin-01': 0.1, 'fin-02': 0.2, 'fin-03': 0.15 };

        function createExperimentNode() {
            experimentCounter++;
            const id = experimentCounter;
            const newExperimentFragment = experimentTemplate.content.cloneNode(true);
            const experimentColumn = newExperimentFragment.querySelector('.experiment-column');
            experimentColumn.dataset.id = id;
            experimentColumn.querySelector('.experiment-title').textContent = `实验 ${id}`;
            appState.experiments.set(id.toString(), { element: experimentColumn });
            
            const strategySelect = experimentColumn.querySelector('.retrieval-strategy');
            const embeddingSelectContainer = experimentColumn.querySelector('.embedding-model').parentElement;
            strategySelect.addEventListener('change', () => {
                if (strategySelect.value === 'keyword') {
                    embeddingSelectContainer.classList.add('opacity-50');
                    embeddingSelectContainer.querySelector('select').disabled = true;
                } else {
                    embeddingSelectContainer.classList.remove('opacity-50');
                    embeddingSelectContainer.querySelector('select').disabled = false;
                }
            });

            return experimentColumn;
        }

        function createAddButton() {
            const addButtonContainer = document.createElement('div');
            addButtonContainer.innerHTML = `
                <button id="add-experiment-btn" class="w-full h-full rounded-xl flex flex-col items-center justify-center p-6 min-h-[400px] transition-all hover-lift">
                    <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="text-indigo-400 mb-4"><path d="M12 5v14m-7-7h14"/></svg>
                    <span class="text-lg font-medium text-gray-600">添加实验</span>
                    <span class="text-sm text-gray-400 mt-1">创建新的对比实验</span>
                </button>
            `;
            experimentsContainer.appendChild(addButtonContainer);
            document.getElementById('add-experiment-btn').addEventListener('click', () => {
                 experimentsContainer.insertBefore(createExperimentNode(), addButtonContainer);
            });
        }

        experimentsContainer.addEventListener('click', (e) => {
            const removeBtn = e.target.closest('.remove-experiment-btn');
            if (removeBtn) {
                const column = removeBtn.closest('.experiment-column');
                appState.experiments.delete(column.dataset.id);
                column.remove();
            }

            const sectionToggle = e.target.closest('.section-toggle');
            if (sectionToggle) {
                const content = sectionToggle.nextElementSibling;
                const icon = sectionToggle.querySelector('.toggle-icon');
                content.classList.toggle('hidden');
                icon.classList.toggle('-rotate-90'); 
            }
        });

        mainFlowBtn.addEventListener('click', () => {
            switch(appState.step) {
                case 'initial':
                    handleRecallRerankAndGenerateStep();
                    break;
                case 'recalled':
                    handleScoringStep();
                    break;
                case 'scored':
                    // 已完成状态，按钮已禁用，无需处理
                    break;
            }
        });

        resetBtn.addEventListener('click', () => {
            resetAll();
        });

        function handleRecallRerankAndGenerateStep() {
            showLoader(mainFlowBtn, '正在运行...');
            runStatus.innerHTML = '<span class="status-indicator status-running"></span>第一步：为所有实验执行召回、重排和生成...';
            
            document.querySelectorAll('.experiment-column').forEach(col => {
                col.querySelector('.results-container').classList.add('hidden');
                col.querySelector('.placeholder-container').classList.remove('hidden');
                col.querySelector('.recall-metrics').classList.add('hidden');
                col.querySelector('.rerank-metrics').classList.add('hidden');
            });
            appState.uniqueDocs.clear();
            const selectedLLMs = Array.from(document.querySelectorAll('.llm-checkbox:checked')).map(el => el.value);

            setTimeout(() => {
                const knowledgeBaseKey = document.getElementById('knowledge-base').value;
                appState.experiments.forEach((exp, id) => {
                    const column = exp.element;
                    // Recall
                    let recalledDocs = [...mockKnowledgeBase[knowledgeBaseKey]].sort(() => 0.5 - Math.random()).slice(0, 10);
                    exp.recalledDocs = recalledDocs;
                    recalledDocs.forEach(doc => appState.uniqueDocs.set(doc.id, doc));

                    column.querySelector('.placeholder-container').classList.add('hidden');
                    column.querySelector('.results-container').classList.remove('hidden');
                    const recallList = column.querySelector('.recall-list');
                    recallList.innerHTML = recalledDocs.map((doc, i) => createDocumentItem(doc, i, null, null, column)).join('');

                    // Rerank
                    const rerankModel = column.querySelector('.rerank-model').value;
                    const rerankTopK = parseInt(column.querySelector('.rerank-topk').value, 10);
                    const rerankThreshold = parseFloat(column.querySelector('.rerank-threshold').value);
                    let rerankedDocs = exp.recalledDocs;
                    if (rerankModel !== '不使用重排') {
                        rerankedDocs = [...exp.recalledDocs].sort((a, b) => (mockGoldenScoresData[b.id] || 0) - (mockGoldenScoresData[a.id] || 0));
                    }
                    const finalReranked = rerankedDocs.map(doc => ({
                        ...doc,
                        rerankScore: rerankModel !== '不使用重排' ? Math.random() * (0.999 - 0.400) + 0.400 : null
                    }))
                    .filter(doc => doc.rerankScore === null || doc.rerankScore >= rerankThreshold)
                    .slice(0, rerankTopK);
                    exp.rerankedDocs = finalReranked;
                    const rerankList = column.querySelector('.rerank-list');
                    rerankList.innerHTML = finalReranked.map((doc, i) => createDocumentItem(doc, i, null, doc.rerankScore, column)).join('');

                    // Generate
                    const generationOutput = column.querySelector('.generation-output');
                    let answersHtml = '';
                    selectedLLMs.forEach(llm => {
                        const mockAnswer = `(${llm}) 基于 ${rerankModel.split('-')[0]} 模型，年假政策核心是：入职满一年享10天年假。`;
                        answersHtml += `<div class="p-2 border-t first:border-t-0"><h6 class="font-semibold text-indigo-700 text-sm">${llm}</h6><p class="text-xs mt-1">${mockAnswer}</p></div>`;
                    });
                    if (generationOutput) {
                        generationOutput.innerHTML = answersHtml;
                    }
                });

                appState.step = 'recalled';
                const btnContent = `
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>
                    对 ${appState.uniqueDocs.size} 个文档进行AI打分 (用于评估)
                `;
                hideLoader(mainFlowBtn, btnContent);
                mainFlowBtn.className = 'btn-secondary flex items-center justify-center transition-all duration-300';
                resetBtn.classList.remove('hidden'); // 显示重置按钮
                runStatus.innerHTML = `<span class="status-indicator status-success"></span>运行完成！共找到 ${appState.uniqueDocs.size} 个不重复的文档待评分。`;
            }, 2000);
        }

        function handleScoringStep() {
            showLoader(mainFlowBtn, '正在打分...');
            runStatus.innerHTML = '<span class="status-indicator status-running"></span>第二步：获取黄金标准分数以完成评估...';
            
            setTimeout(() => {
                appState.goldenScores = mockGoldenScoresData;

                appState.experiments.forEach((exp, id) => {
                    const column = exp.element;
                    
                    // Update recall list with scores and show metrics
                    const recallList = column.querySelector('.recall-list');
                    if (recallList) {
                        recallList.innerHTML = exp.recalledDocs.map((doc, i) => createDocumentItem(doc, i, appState.goldenScores[doc.id], null, column)).join('');
                    }
                    displayMetrics(column.querySelector('.recall-metrics'), '召回评估', { 'Recall@10': `${(Math.random() * (85 - 75) + 75).toFixed(1)}%`, 'MRR': `${(Math.random() * (0.95 - 0.85) + 0.85).toFixed(2)}` });

                    // Update rerank list with scores and show metrics
                    const rerankList = column.querySelector('.rerank-list');
                    if (rerankList) {
                        rerankList.innerHTML = exp.rerankedDocs.map((doc, i) => createDocumentItem(doc, i, appState.goldenScores[doc.id], doc.rerankScore, column)).join('');
                    }
                    displayMetrics(column.querySelector('.rerank-metrics'), '重排评估', { 'NDCG@K': `${(Math.random() * (0.98 - 0.88) + 0.88).toFixed(2)}`, '命中率@K': `${(Math.random() > 0.1 ? '100' : '80')}%` });
                });

                appState.step = 'scored';
                const btnContent = `
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M9 12l2 2 4-4"/><circle cx="12" cy="12" r="10"/></svg>
                    评估已完成
                `;
                hideLoader(mainFlowBtn, btnContent);
                mainFlowBtn.className = 'btn-primary bg-green-500 hover:bg-green-600 flex items-center justify-center transition-all duration-300';
                mainFlowBtn.disabled = true; // 禁用主按钮，因为已经完成
                runStatus.innerHTML = '<span class="status-indicator status-success"></span>评估完成！可以查看下方各实验的详细结果。';
            }, 2000);
        }

        function resetAll() {
            appState.step = 'initial';
            runStatus.innerHTML = '';
            const btnContent = `
                <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
                运行所有实验
            `;
            mainFlowBtn.innerHTML = btnContent;
            mainFlowBtn.className = 'btn-primary flex items-center justify-center transition-all duration-300';
            mainFlowBtn.disabled = false; // 重新启用主按钮
            resetBtn.classList.add('hidden'); // 隐藏重置按钮
            
            document.querySelectorAll('.experiment-column').forEach(col => {
                col.querySelector('.results-container').classList.add('hidden');
                col.querySelector('.placeholder-container').classList.remove('hidden');
            });
        }

        function createDocumentItem(doc, index, goldenScore = null, rerankScore = null, column) {
            let sourceTagHtml = '';
            const strategySelect = column.querySelector('.retrieval-strategy');
            if (rerankScore === null && strategySelect && strategySelect.value === 'hybrid' && doc.source) {
                const tagMap = {
                    'vector': { text: 'V', style: '--tag-bg-start: #f3e8ff; --tag-bg-end: #e9d5ff; --tag-text: #7c3aed;', title: '向量检索来源' },
                    'keyword': { text: 'K', style: '--tag-bg-start: #dbeafe; --tag-bg-end: #bfdbfe; --tag-text: #2563eb;', title: '关键词检索来源' },
                    'both': { text: 'V+K', style: '--tag-bg-start: #dcfce7; --tag-bg-end: #bbf7d0; --tag-text: #16a34a;', title: '向量与关键词共同来源' }
                };
                const tag = tagMap[doc.source];
                if (tag) {
                    sourceTagHtml = `<span class="source-tag" style="${tag.style}" title="${tag.title}">${tag.text}</span>`;
                }
            }
            
            let scoreHtml = '';
            if (goldenScore !== null) {
                const goldenScoreColor = goldenScore > 0.8 ? 'text-green-600' : goldenScore > 0.5 ? 'text-yellow-600' : 'text-red-600';
                scoreHtml += `<div class="text-xs text-gray-500">相关性</div><div class="font-bold ${goldenScoreColor}">${goldenScore.toFixed(2)}</div>`;
            }
            if (rerankScore !== null) {
                const rerankScoreColor = rerankScore > 0.8 ? 'text-blue-600' : rerankScore > 0.5 ? 'text-cyan-600' : 'text-gray-500';
                scoreHtml += `<div class="text-xs text-gray-500 mt-1">重排分</div><div class="font-bold ${rerankScoreColor}">${rerankScore.toFixed(3)}</div>`;
            }
            if (scoreHtml === '' && goldenScore === null) {
                scoreHtml = `<div class="score-placeholder h-5"></div>`;
            }

            return `
                <div class="result-card flex items-start space-x-4 p-3 hover-lift">
                    <div class="flex-shrink-0 h-6 w-6 rounded-full bg-gradient-to-br from-indigo-400 to-purple-500 text-white flex items-center justify-center text-xs font-bold shadow-sm">${index + 1}</div>
                    <div class="flex-grow">
                        <p class="text-sm text-gray-700 leading-relaxed flex items-center">${doc.content}${sourceTagHtml}</p>
                    </div>
                    <div class="flex-shrink-0 w-20 text-right text-xs score-display">
                        ${scoreHtml}
                    </div>
                </div>
            `;
        }

        function displayMetrics(container, title, metrics) {
            if (!container) {
                console.error("Metrics container not found for title:", title);
                return;
            }
            let metricsHtml = `<h6 class="font-semibold text-xs mb-2">${title}</h6><div class="grid grid-cols-2 gap-2 text-center">`;
            for (const [key, value] of Object.entries(metrics)) {
                metricsHtml += `<div><div class="text-lg font-bold text-indigo-600">${value}</div><div class="text-xs text-gray-500">${key}</div></div>`;
            }
            metricsHtml += `</div>`;
            container.innerHTML = metricsHtml;
            container.classList.remove('hidden');
        }

        function showLoader(button, text) {
            button.disabled = true;
            button.innerHTML = `<div class="gradient-loader mr-2"></div> <span class="text-sm">${text}</span>`;
            button.style.opacity = '0.8';
        }

        function hideLoader(button, originalContent) {
            button.disabled = false;
            button.innerHTML = originalContent;
            button.style.opacity = '1';
        }

        // Initial setup
        const initialNode = createExperimentNode();
        experimentsContainer.appendChild(initialNode);
        
        const secondNode = createExperimentNode();
        experimentsContainer.appendChild(secondNode);
        
        // 设置第一个实验的配置
        const initialStrategySelect = initialNode.querySelector('.retrieval-strategy');
        if (initialStrategySelect) {
            initialStrategySelect.value = 'hybrid'; 
            initialStrategySelect.dispatchEvent(new Event('change'));
        }
        
        const initialRerankSelect = initialNode.querySelector('.rerank-model');
        if (initialRerankSelect) {
            initialRerankSelect.value = 'BGE-Reranker-large'; // 第一个实验默认BGE
        }
        
        // 设置第二个实验的配置
        const secondStrategySelect = secondNode.querySelector('.retrieval-strategy');
        if (secondStrategySelect) {
            secondStrategySelect.value = 'vector';
            secondStrategySelect.dispatchEvent(new Event('change'));
        }

        const secondEmbeddingSelect = secondNode.querySelector('.embedding-model');
        if (secondEmbeddingSelect) {
            secondEmbeddingSelect.selectedIndex = 1; 
        }
        
        const secondRerankSelect = secondNode.querySelector('.rerank-model');
        if (secondRerankSelect) {
            secondRerankSelect.value = 'Qwen3_4B-Reranker'; // 第二个实验默认Qwen
        }
        
        createAddButton();
    });
    </script>
</body>
</html>
